using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using CodRevit.Views;

namespace CodRevit.Commands
{
    [Transaction(TransactionMode.Manual)]
    public class EncryptFamilyCommand : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIDocument uidoc = commandData.Application.ActiveUIDocument;
            Document doc = uidoc.Document;

            // 检查是否在族文档中
            if (!doc.IsFamilyDocument)
            {
                TaskDialog.Show("错误", "此命令只能在族文档中使用。");
                return Result.Failed;
            }

            var inputDialog = new InputKeyWindow(true);
            if (inputDialog.ShowDialog() != true || !inputDialog.IsConfirmed)
            {
                return Result.Cancelled;
            }

            string key = inputDialog.Key;
            if (string.IsNullOrWhiteSpace(key))
            {
                TaskDialog.Show("错误", "密钥不能为空。");
                return Result.Failed;
            }

            try
            {
                using (Transaction trans = new Transaction(doc, "族加密"))
                {
                    trans.Start();

                    // 隐藏几何图形
                    HideGeometry(doc);

                    // 加密参数
                    EncryptParameters(doc, key);

                    trans.Commit();
                }

                TaskDialog.Show("完成", "族加密完成。");
                return Result.Succeeded;
            }
            catch (Exception ex)
            {
                message = ex.Message;
                return Result.Failed;
            }
        }

        private void HideGeometry(Document doc)
        {
            // 创建过滤器集合
            List<ElementFilter> filters = new List<ElementFilter>
            {
                new ElementClassFilter(typeof(Extrusion)),
                new ElementClassFilter(typeof(Blend)),
                new ElementClassFilter(typeof(Revolution)),
                new ElementClassFilter(typeof(Sweep)),
                new ElementClassFilter(typeof(SweptBlend)),
                new ElementClassFilter(typeof(FamilyInstance)),
                new ElementClassFilter(typeof(Form)),
                new ElementClassFilter(typeof(RoofBase)),
                new ElementClassFilter(typeof(ReferencePlane)),
                new ElementClassFilter(typeof(Dimension)),
                new ElementClassFilter(typeof(AngularDimension)),
                new ElementClassFilter(typeof(CurveElement)),
                new ElementClassFilter(typeof(GeomCombination))
            };

            LogicalOrFilter orFilter = new LogicalOrFilter(filters);

            // 获取所有几何图形元素
            var collector = new FilteredElementCollector(doc)
                .WhereElementIsNotElementType()
                .WherePasses(orFilter);

            // 获取所有视图
            var views = new FilteredElementCollector(doc)
                .OfClass(typeof(View))
                .Cast<View>()
                .Where(v => !v.IsTemplate && v.CanBePrinted &&
                    (v.ViewType == ViewType.FloorPlan ||
                     v.ViewType == ViewType.CeilingPlan ||
                     v.ViewType == ViewType.Elevation ||
                     v.ViewType == ViewType.Section ||
                     v.ViewType == ViewType.ThreeD));

            // 获取所有元素ID
            var allElements = collector
                .Where(e => !(e is CurveElement) ||
                    (e is CurveElement ce &&
                    (ce.Category?.Name == "参照线" ||
                     ce.Category?.Name == "Reference Lines")))
                .Select(e => e.Id)
                .ToList();

            // 在每个视图中隐藏元素
            foreach (View view in views)
            {
                var validElements = allElements
                    .Where(id => doc.GetElement(id)?.CanBeHidden(view) == true)
                    .ToList();

                if (validElements.Any())
                {
                    view.HideElements(validElements);
                }
            }
        }

        private void EncryptParameters(Document doc, string key)
        {
            FamilyManager familyManager = doc.FamilyManager;
            foreach (FamilyParameter param in familyManager.Parameters)
            {
                if (!param.IsReporting &&
                    param.Definition is InternalDefinition internalDef &&
                    internalDef.BuiltInParameter == BuiltInParameter.INVALID)
                {
                    string originalName = param.Definition.Name;
                    string encryptedName = EncryptParameterName(originalName, key);
                    familyManager.RenameParameter(param, encryptedName);
                }
            }
        }

        private string EncryptParameterName(string name, string key)
        {
            using (Aes aes = Aes.Create())
            {
                aes.Key = GenerateKey(key);
                aes.IV = new byte[16];

                using (var encryptor = aes.CreateEncryptor())
                using (var msEncrypt = new System.IO.MemoryStream())
                {
                    using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    using (var swEncrypt = new System.IO.StreamWriter(csEncrypt))
                    {
                        swEncrypt.Write(name);
                    }

                    return Convert.ToBase64String(msEncrypt.ToArray())
                        .Replace("/", "_")
                        .Replace("+", "-")
                        .Replace("=", "");
                }
            }
        }

        private byte[] GenerateKey(string password)
        {
            using (var deriveBytes = new Rfc2898DeriveBytes(password,
                new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 },
                1000))
            {
                return deriveBytes.GetBytes(32);
            }
        }
    }
}